﻿using System;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Threading;
using BiliBiliLiveDanMuTool.Utils;
using LibNet.Http;
using LibPlug.Model;

namespace BiliBiliLiveDanMuTool.DanMuClient
{
    public class DanMuClient : IDisposable
    {
        /// <summary>
        /// 动态缓冲区默认大小 10K
        /// </summary>
        private const int DefaultBufferSize = 10 * 1024;
        /// <summary>
        /// 数据包头长度
        /// </summary>
        private const int PacketHeadLenght = 16;
        /// <summary>
        /// 默认弹幕服务器地址
        /// </summary>
        private const string m_chatHost = @"livecmt-1.bilibili.com";
        /// <summary>
        /// 弹幕服务器端口
        /// </summary>
        private const int m_port = 788;
        /// <summary>
        /// XML文档URL路径
        /// </summary>
        private const string m_xmlUrlPre = @"http://live.bilibili.com/api/player?id=cid:";

        private TcpClient m_client;
        private string m_server;

        private long m_roomId;
        private long m_uid;
        private bool m_connected = false;
        /// <summary>
        /// 是否建立了TCP连接
        /// </summary>
        public bool Connected { get { return m_connected; } }
        private Thread m_recviveThread, m_heartBeatThread = null;

        public delegate void MessageCallBack(string message);
        public delegate void DanMuCallBack(DanMuMessageModel model);
        public MessageCallBack PeopleNumber { get; set; }
        public DanMuCallBack DanMu { get; set; }

        /// <summary>
        /// 连接到bilibili直播间
        /// </summary>
        /// <param name="roomUrl">直播间URL</param>
        /// <returns></returns>
        public bool ConnectBilibili(string roomUrl)
        {
            if(getRoomId(roomUrl))
            {
                if (m_client != null && m_client.Connected) m_client.Close();
                m_client = new TcpClient();
                m_client.Connect(m_server, m_port);
                if(joinRoom())
                {
                    m_connected = true;
                    m_heartBeatThread = new Thread(heartBeatThread);
                    m_heartBeatThread.Start();
                    m_recviveThread = new Thread(recviveLoop);
                    m_recviveThread.SetApartmentState(ApartmentState.STA);
                    m_recviveThread.IsBackground = true;
                    m_recviveThread.Start();
                }
            }
            return false;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnect()
        {
            m_connected = false;
        }

        #region > 私有函数 <

        /// <summary>
        /// 获得直播间的真实ID
        /// </summary>
        /// <param name="roomUrl"></param>
        /// <returns></returns>
        private bool getRoomId(string roomUrl)
        {
            using (HttpClient _client = new HttpClient())
            {
                using (HttpResponseMessage _msg = _client.SendAsync(new HttpRequestMessage()
                {
                    Method = HttpMethod.Get,
                    RequestUri = new Uri(roomUrl)
                }).Result.EnsureSuccessStatusCode())
                {
                    #region > 解析弹幕服务器地址 <
                    string _result = _msg.Content.ReadAsStringAsync().Result;
                    // 分组筛选
                    Regex _reg = new Regex(@"ROOMID\s=\s(\d+)");
                    m_roomId = long.Parse(_reg.Match(_result).Groups[1].Value);
                    // 解析弹幕服务器地址
                    string _xmlContent = new NetUtils().HttpGet(m_xmlUrlPre + m_roomId, "", Encoding.UTF8);
                    XmlDocument _xml = new XmlDocument();
                    _xml.LoadXml(@"<root>" + _xmlContent + @"</root>");
                    XmlNodeList _xnList = _xml.SelectSingleNode("root").ChildNodes;
                    foreach(XmlNode node in _xnList)
                    {
                        var _xe = (XmlElement)node;
                        if (_xe.Name.Equals("server"))
                        {
                            m_server = _xe.InnerText;
                            return true;
                        }
                    }
                    return false;
                    #endregion
                }
                
            }
        }

        /// <summary>
        /// 加入指定直播间
        /// </summary>
        /// <returns></returns>
        private bool joinRoom()
        {
            long _uid = m_uid = 1000000000 + (long)(2000000000 * new Random().NextDouble());
            string _jsonReq = "{\"roomid\":" + m_roomId + ",\"uid\":" + _uid + "}";
            sendPacket(_jsonReq.Length + PacketHeadLenght, PacketHeadLenght, 1,7,1,_jsonReq);
            return true;
        }

        /// <summary>
        /// 弹幕服务器数据包发送
        /// </summary>
        /// <param name="totalLenght">数据包长度</param>
        /// <param name="headLenght">头部数据长度</param>
        /// <param name="version">协议版本</param>
        /// <param name="action">操作标识符</param>
        /// <param name="param5">参数</param>
        /// <param name="body">数据包内容</param>
        private void sendPacket(int totalLenght,short headLenght,short version,int action,int param5 = 1,string body=null)
        {
            DynamicBufferManager _buffer = new DynamicBufferManager(16);
            _buffer.WriteInt(totalLenght, true); // 4
            _buffer.WriteShort(headLenght, true); // 2
            _buffer.WriteShort(version, true); // 2
            _buffer.WriteInt(action,true); // 4
            _buffer.WriteInt(action, true); // 4
            if (!string.IsNullOrEmpty(body)) _buffer.WriteString(body, true);
            m_client.Client.Send(_buffer.Buffer,0,_buffer.DataCount,SocketFlags.None);
        }

        /// <summary>
        /// 心跳数据包线程
        /// </summary>
        private void heartBeatThread()
        {
            while(m_connected)
            {
                Thread.Sleep(2000);
                sendPacket(PacketHeadLenght, PacketHeadLenght, 1, 2);
            }
        }

        /// <summary>
        /// 数据接收线程
        /// </summary>
        private void recviveLoop()
        {
            DynamicBufferManager _recvBuffer = new DynamicBufferManager(DefaultBufferSize);
            while(m_connected)
            {
                byte[] _recvData = new byte[DefaultBufferSize];
                int _recvLenght = m_client.Client.Receive(_recvData, SocketFlags.None);
                if(_recvLenght > 0 && m_connected)
                {
                    _recvBuffer.WriteBuffer(_recvData, 0, _recvLenght);
                    analyzeData(_recvBuffer);
                    _recvBuffer.Clear();
                }
            }
        }

        /// <summary>
        /// 解析数据
        /// </summary>
        /// <param name="data">待解析数据</param>
        private void analyzeData(DynamicBufferManager data)
        {
            int _dataLenght = data.DataCount;
            if(_dataLenght >= PacketHeadLenght)
            {
                int _msgLenght = data.ReadToInt(true);
                if(_msgLenght > PacketHeadLenght && _msgLenght == _dataLenght)
                {
                    data.DataPoint += 4;
                    int action = data.ReadToInt(true) - 1;
                    if (action == 2)
                    {
                        data.DataPoint += 4;
                        PeopleNumber(data.ReadToInt(true).ToString());
                    }
                    else if(action == 4)
                    {
                        int _bodyLenght = _dataLenght - PacketHeadLenght;
                        string _msg = Encoding.UTF8.GetString(data.Buffer, PacketHeadLenght, _bodyLenght);
                        var _danmu = new DataAssembler(_msg).GetMessage();
                        if(_danmu != null)
                        {
                            DanMu(_danmu);
                            GlobalMember.DanMuPluginsManager.broadcastReceiveMesage(_danmu);
                        }
                    }else
                    {
                        // 保留
                    }
                }else if(_msgLenght > PacketHeadLenght && _msgLenght < _dataLenght) // 数据截断处理
                {
                    #region > 注释 <
                    /*
                       接收到的总数据424字节 
                       解析到的消息长度为217字节
                       说明消息长度有问题。
                       从总数据复制 217 字节到 singalData
                       调用解析
                       424 - 217 = 207
                       从总数据再复制 207 字节到 _remainData，位置从217开始
                       调用解析
                     */
                    #endregion
                    DynamicBufferManager _buffer = new DynamicBufferManager(1);
                    _buffer.WriteBuffer(data.Buffer,0,_msgLenght);
                    analyzeData(_buffer);
                    _buffer.Clear();
                    _buffer.WriteBuffer(data.Buffer, _msgLenght, _dataLenght - _msgLenght);
                    analyzeData(_buffer);
                    _buffer.Clear();
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }
        ~DanMuClient()
        {
            Dispose(false);
        }
        protected void Dispose(bool disposing)
        {
            if (disposing)
            {
                DisConnect();
            }
        }
        #endregion
    }
}
